#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;


namespace NMemoryManagerTest
{
	TEST_CLASS(NMemoryChunkTest)
	{
	public:
		
		TEST_METHOD(NBaseMemoryChunk_Alloc_Free)
		{
			// TODO: Your test code here
			CNBaseMemoryChunk baseMemoryChunk;

			Assert::AreEqual( baseMemoryChunk.Create( 100, 10, 10 ), TRUE );

			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(10) );

			void* pItem1 = baseMemoryChunk.Alloc();
			Assert::IsNotNull( pItem1 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(9) );

			void* pItem2 = baseMemoryChunk.Alloc();
			Assert::IsNotNull( pItem2 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(8) );

			void* pItem3 = baseMemoryChunk.Alloc();
			Assert::IsNotNull( pItem3 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(7) );

			void* pItem4 = baseMemoryChunk.Alloc();
			Assert::IsNotNull( pItem4 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(6) );

			void* pItem5 = baseMemoryChunk.Alloc();
			Assert::IsNotNull( pItem5 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(5) );


			baseMemoryChunk.Free( pItem1 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(6) );

			baseMemoryChunk.Free( pItem2 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(7) );

			baseMemoryChunk.Free( pItem3 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(8) );

			baseMemoryChunk.Free( pItem4 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(9) );

			baseMemoryChunk.Free( pItem5 );
			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(10) );

			baseMemoryChunk.Destroy();
		}



		TEST_METHOD(NBaseMemoryChunk_AllocateEnough)
		{
			CNBaseMemoryChunk baseMemoryChunk;

			Assert::AreEqual( baseMemoryChunk.Create( 100, 1024, 1024 ), TRUE );

			const int cnLoopCount = 1000000;

			std::vector<void*> vecContainer;
			vecContainer.reserve( cnLoopCount );

			for ( int nCur = 0; nCur < cnLoopCount; ++nCur )
			{
				vecContainer.push_back( baseMemoryChunk.Alloc() );
			}

			for ( void*& pMem : vecContainer )
			{
				baseMemoryChunk.Free( pMem );
			}

			baseMemoryChunk.Destroy();
		}



		TEST_METHOD(NBaseMemoryChunk_OverAllocate)
		{
			CNBaseMemoryChunk baseMemoryChunk;

			Assert::AreEqual( baseMemoryChunk.Create( 100, 3, 0 ), TRUE );

			Assert::IsNotNull( baseMemoryChunk.Alloc() );
			Assert::IsNotNull( baseMemoryChunk.Alloc() );
			Assert::IsNotNull( baseMemoryChunk.Alloc() );

			Assert::IsNull( baseMemoryChunk.Alloc() );

			baseMemoryChunk.Destroy();
		}



		TEST_METHOD(NBaseMemoryChunk_OverAllocate_Expansion)
		{
			CNBaseMemoryChunk baseMemoryChunk;

			Assert::AreEqual( baseMemoryChunk.Create( 100, 3, 3 ), TRUE );

			std::list<void*> MemList;

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(2) );
			}

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(1) );
			}

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(0) );
			}


			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(2) );
			}

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(1) );
			}

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(0) );
			}

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(2) );
			}

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(1) );
			}

			{
				void* pMem = baseMemoryChunk.Alloc();
				Assert::IsNotNull( pMem );
				MemList.push_back( pMem );
				Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(0) );
			}

			for ( void*& pMem : MemList )
			{
				baseMemoryChunk.Free( pMem );
			}

			Assert::AreEqual( baseMemoryChunk.GetFreeItemCount(), size_t(9) );

			baseMemoryChunk.Destroy();
		}



		TEST_METHOD(NStaticMemoryChunk_int_Alloc_Free)
		{
			// TODO: Your test code here

			static const int cnInitialValue = 10;
			static const int cnFinalValue = 3;

			typedef struct _Obj
			{
				_Obj()
				{
					_nValue = cnInitialValue;
				}

				~_Obj()
				{
					_nValue = cnFinalValue;
				}

				int _nValue;
			} OBJ;

			CNStaticObjectChunkT<OBJ>	objectChunk;

			Assert::AreEqual( objectChunk.Create( 10 ), TRUE );

			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(10) );

			auto pObj1 = objectChunk.Alloc();
			Assert::AreEqual( pObj1->_nValue, cnInitialValue );
			Assert::IsNotNull( pObj1 );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(9) );

			auto pObj2 = objectChunk.Alloc();
			Assert::AreEqual( pObj2->_nValue, cnInitialValue );
			Assert::IsNotNull( pObj2 );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(8) );

			auto pObj3 = objectChunk.Alloc();
			Assert::AreEqual( pObj3->_nValue, cnInitialValue );
			Assert::IsNotNull( pObj3 );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(7) );

			auto pObj4 = objectChunk.Alloc();
			Assert::AreEqual( pObj4->_nValue, cnInitialValue );
			Assert::IsNotNull( pObj4 );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(6) );

			auto pObj5 = objectChunk.Alloc();
			Assert::AreEqual( pObj5->_nValue, cnInitialValue );
			Assert::IsNotNull( pObj5 );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(5) );


			objectChunk.Free( pObj1 );
			Assert::AreEqual( pObj1->_nValue, cnFinalValue );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(6) );

			objectChunk.Free( pObj2 );
			Assert::AreEqual( pObj2->_nValue, cnFinalValue );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(7) );

			objectChunk.Free( pObj3 );
			Assert::AreEqual( pObj3->_nValue, cnFinalValue );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(8) );

			objectChunk.Free( pObj4 );
			Assert::AreEqual( pObj4->_nValue, cnFinalValue );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(9) );

			objectChunk.Free( pObj5 );
			Assert::AreEqual( pObj5->_nValue, cnFinalValue );
			Assert::AreEqual( objectChunk.GetFreeItemCount(), size_t(10) );

			objectChunk.Destroy();
		}



		TEST_METHOD(NStaticMemoryChunk_int_OverAllocate)
		{
			CNStaticObjectChunkT<int>	intObjectChunk;

			Assert::AreEqual( intObjectChunk.Create( 3 ), TRUE );

			Assert::IsNotNull( intObjectChunk.Alloc() );
			Assert::IsNotNull( intObjectChunk.Alloc() );
			Assert::IsNotNull( intObjectChunk.Alloc() );

			Assert::IsNull( intObjectChunk.Alloc() );

			intObjectChunk.Destroy();
		}

	};
}